/*
 * Copyright (c) 2010. Dylan Schell.
 */

package nl.xs4all.dylanschell.deckprinter.services;

import com.google.inject.Inject;
import com.google.inject.Singleton;
import nl.xs4all.dylanschell.deckprinter.model.CardInSet;
import nl.xs4all.dylanschell.deckprinter.model.DeckDescriptor;
import nl.xs4all.dylanschell.deckprinter.model.SetDescriptor;
import org.octgn.cardset.Set;
import org.openxmlformats.schemas._package._2006.relationships.CTRelationship;
import org.openxmlformats.schemas._package._2006.relationships.CTRelationships;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

@Singleton
public class OCTGNLoader implements SetLoader
{
    private static final Logger logger = Logger.getLogger(OCTGNLoader.class.getName());

    private static final String HTTP_SCHEMAS_OCTGN_ORG_SET_DEFINITION = "http://schemas.octgn.org/set/definition";
    private static final String HTTP_SCHEMAS_OCTGN_ORG_SET_DECK = "http://schemas.octgn.org/set/deck";
    private static final String HTTP_SCHEMAS_OCTGN_ORG_PICTURE = "http://schemas.octgn.org/picture";
    private final JAXBContext ctx;
    private final XMLInputFactory inputFactory;
    private final DeckService deckService;
    private final DeckParser deckParser;

    @Inject
    public OCTGNLoader(DeckService deckService, DeckParser deckParser, JAXBContext ctx, XMLInputFactory inputFactory)
    {
        this.deckParser = deckParser;
        this.deckService = deckService;
        this.ctx = ctx;
        this.inputFactory = inputFactory;
    }

    public SetDescriptor loadSet(File file) throws IOException
    {
        ZipFile zipFile = new ZipFile(file);
        CTRelationships relationships = loadElement(zipFile, "/_rels/.rels", CTRelationships.class);
        SetDescriptor result = null;
        if (relationships != null)
        {
            for (CTRelationship rel : relationships.getRelationship())
            {
                if (HTTP_SCHEMAS_OCTGN_ORG_SET_DEFINITION.equals(rel.getType()))
                {
                    Set set = loadElement(zipFile, rel.getTarget(), Set.class);
                    if (result == null)
                    {
                        result = new SetDescriptor(file, UUID.fromString(set.getId()), set.getName());
                    }
                    if (set.getCards() != null)
                    {
                        for (Set.Cards.Card c : set.getCards().getCard())
                        {
                            CardInSet cis = parseCard(c, result.getId());

                            result.registerCard(cis.getId(), cis);
                        }
                    }
                    CTRelationships setRels = loadElement(zipFile, "/_rels" + rel.getTarget() + ".rels", CTRelationships.class);
                    if (setRels != null)
                    {
                        for (CTRelationship setRel : setRels.getRelationship())
                        {
                            if (HTTP_SCHEMAS_OCTGN_ORG_PICTURE.equals(setRel.getType()))
                            {
                                String uuid = setRel.getId().toLowerCase();
                                if (uuid.length() > 32)
                                {
                                    uuid = uuid.substring(1);
                                }
                                if (uuid.indexOf('-') < 0)
                                {
                                    uuid = uuid.substring(0, 8)
                                            + "-"
                                            + uuid.substring(8, 12)
                                            + "-"
                                            + uuid.substring(12, 16)
                                            + "-"
                                            + uuid.substring(16, 20)
                                            + "-"
                                            + uuid.substring(20);
                                }
                                try
                                {
                                    UUID id = UUID.fromString(uuid);
                                    result.registerImage(
                                            id,
                                            setRel.getTarget());
                                } catch (NumberFormatException nfe)
                                {
                                    logger.fine(
                                            rel.getId()
                                                    + " is not a valid UUID. (target: "
                                                    + setRel.getTarget() + ")");
                                }
                            }
                        }
                    }
                }
            }
            for (CTRelationship rel : relationships.getRelationship())
            {
                if (HTTP_SCHEMAS_OCTGN_ORG_SET_DECK.equals(rel.getType()))
                {
                    ZipEntry entry = zipFile.getEntry(rel.getTarget().substring(1));
                    if (entry != null)
                    {
                        InputStream is = zipFile.getInputStream(entry);
                        try
                        {
                            DeckDescriptor descriptor = deckParser.parseDeck(is, rel.getTarget());
                            deckService.registerDeck(descriptor);
                        } finally
                        {
                            is.close();
                        }
                    }
                }
            }

        }
        zipFile.close();
        return result;
    }

    private CardInSet parseCard(Set.Cards.Card c, UUID setId)
    {
        UUID cardId = UUID.fromString(c.getId());
        CardInSet result = new CardInSet(cardId, setId, c.getName());
        for (Set.Cards.Card.Property p : c.getProperty())
        {
            if ("Cost".equals(p.getName()))
            {
                result.setCost(p.getValue());
            } else if ("Converted Mana Cost".equals(p.getName()))
            {
                result.setConvertedManaCost(p.getValue());
            } else if ("Color".equals(p.getName()))
            {
                result.setColor(p.getValue());

            } else if ("Type".equals(p.getName()))
            {
                result.setType(p.getValue());
            } else if ("Subtype".equals(p.getName()))
            {
                result.setSubType(p.getValue());
            } else if ("Rarity".equals(p.getName()))
            {
                result.setRarity(p.getValue());
            } else if ("Rules".equals(p.getName()))
            {
                result.setRules(p.getValue());
            } else if ("Flavor".equals(p.getName()))
            {
                result.setFlavor(p.getValue());
            } else if ("Power".equals(p.getName()))
            {
                result.setPower(p.getValue());
            } else if ("Toughness".equals(p.getName()))
            {
                result.setToughness(p.getValue());
            } else if ("PT Box".equals(p.getName()))
            {
                result.setPtBox(p.getValue());
            } else if ("Artist".equals(p.getName()))
            {
                result.setArtist(p.getValue());
            } else if ("MultiverseId".equals(p.getName()))
            {
                result.setMutiverseId(p.getValue());
            } else if ("Number".equals(p.getName()))
            {
                result.setNumber(p.getValue());
            } else
            {
                System.out.println("unknown property " + p.getName());
            }
        }
        return result;
    }

    private <T> T loadElement(ZipFile file, String target, Class<T> elementClass)
    {
        ZipEntry entry = file.getEntry(target.substring(1));
        if (entry != null)
        {
            InputStream is;
            try
            {
                is = file.getInputStream(entry);
                try
                {
                    XMLStreamReader reader = inputFactory.createXMLStreamReader(is);
                    Unmarshaller unmarshaller = ctx.createUnmarshaller();
                    JAXBElement<T> result = unmarshaller.unmarshal(reader, elementClass);
                    return result.getValue();
                } catch (JAXBException e)
                {
                    e.printStackTrace();
                } catch (XMLStreamException e)
                {
                    e.printStackTrace();
                } finally
                {
                    is.close();
                }
            } catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        return null;
    }
}
